#pragma once

#include "my_event.h"
#include "comm/my_buf.h"
#include "comm/my_log.h"
#include "comm/my_syscall.h"
#include "comm/my_inet.h"

typedef enum {
    MY_CONN_EPOLL_LT,
    MY_CONN_EPOLL_ET,
} my_conn_flag_t;

typedef struct {
    void                *data;
    int                 fd;                 // sockfd
    my_event_t          read;
    my_event_t          write;
    my_conn_flag_t      flag;
    my_epoll_event_t    ee;
    my_queue_t          queue;
    my_event_handler_t  server_handler;     // for http keepalive
    unsigned            start:1;            // start io
    unsigned            error:1;
} my_conn_t;

static inline void
my_conn_init(my_conn_t *conn, my_conn_flag_t flag)
{
    // it's a read event
    conn->read.data = conn;
    conn->read.write = 0;

    // it's a write event
    conn->write.data = conn;
    conn->write.write = 1;

    // epoll event
    conn->flag = flag;
    conn->ee.data.ptr = conn;
    conn->fd = INVALID_FD;
}

static inline my_syscall_result_t
my_conn_read(my_conn_t *conn, void *buff, int64_t count)
{
    my_syscall_result_t res;
    res = my_syscall_read(conn->fd, buff, count);

    if (res.res <= 0) {
        if (res.res != MY_AGAIN) {
            conn->read.error = 1;
        }

        conn->read.ready = 0;
    }

    return res;
}

static inline my_syscall_result_t
my_conn_readv(my_conn_t *conn, my_iovec_t *vector, int64_t count)
{
    my_syscall_result_t res;
    res = my_syscall_readv(conn->fd, vector, (int)count);

    if (res.res <= 0) {
        if (res.res != MY_AGAIN) {
            conn->read.error = 1;
        }

        conn->read.ready = 0;
    }

    return res;
}

static inline my_syscall_result_t
my_conn_write(my_conn_t *conn, void *buff, int64_t count)
{
    my_syscall_result_t res;
    res = my_syscall_write(conn->fd, buff, count);

    if (res.res <= 0) {
        if (res.res != MY_AGAIN) {
            conn->write.error = 1;
        }

        conn->write.ready = 0;
    }

    return res;
}

static inline my_syscall_result_t
my_conn_writev(my_conn_t *conn, my_iovec_t *vector, int64_t count)
{
    my_syscall_result_t res;
    res = my_syscall_writev(conn->fd, vector, (int)count);

    if (res.res <= 0) {
        if (res.res != MY_AGAIN) {
            conn->write.error = 1;
        }

        conn->write.ready = 0;
    }

    return res;
}

static inline void
my_conn_close(my_conn_t *conn)
{
    if (conn->fd == INVALID_FD) {
        MY_LOG_ERROR("invalid sockfd");
        return;
    }

    my_syscall_close(conn->fd);
    conn->fd = INVALID_FD;
}


static inline my_bool_t
my_conn_local_sockaddr_str_get(my_conn_t *conn, char *dst, int dst_size)
{
    int64_t             res;
    my_syscall_result_t sres;
    my_address_t        address;
    socklen_t           address_len = sizeof(my_address_t);

    bzero(&address, sizeof(my_address_t));

    if (conn->fd == INVALID_FD) {
        return MY_FALSE;
    }

    sres = my_syscall_getsockname(conn->fd,
            (my_sockaddr_t *) &address, &address_len);
    if (sres.eno != 0) {
        return MY_FALSE;
    }

    res = my_address_to_str(&address, dst, dst_size);
    if (res == 0) {
        return MY_FALSE;
    }

    return MY_TRUE;
}


static inline my_bool_t
my_conn_peer_sockaddr_str_get(my_conn_t *conn, char *dst, int dst_size)
{
    int64_t             res;
    my_syscall_result_t sres;
    my_address_t        address;
    socklen_t           address_len = sizeof(my_address_t);

    bzero(&address, sizeof(my_address_t));

    if (conn->fd == INVALID_FD) {
        return MY_FALSE;
    }

    sres = my_syscall_getpeername(conn->fd,
            (my_sockaddr_t *) &address, &address_len);
    if (sres.eno != 0) {
        return MY_FALSE;
    }

    res = my_address_to_str(&address, dst, dst_size);
    if (res == 0) {
        return MY_FALSE;
    }

    return MY_TRUE;
}